package wicketcool.converter;

import japa.parser.ASTHelper;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.PackageDeclaration;
import japa.parser.ast.TypeParameter;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.ModifierSet;
import japa.parser.ast.expr.NameExpr;
import japa.parser.ast.type.ClassOrInterfaceType;
import japa.parser.ast.type.Type;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import wicketcool.model.WCLClass;
import wicketcool.model.WCLField;
import wicketcool.model.WCLImport;
import wicketcool.model.method.WCLMethod;
import wicketcool.model.types.WCLGenericType;
import wicketcool.model.types.WCLInterfaceType;

import java.util.ArrayList;
import java.util.List;

/**
 * User: rabbit
 * Date: Jul 29, 2009
 */
@Component
public class DefaultClassModelCompilationUnitConverter implements ClassModelCompilationUnitConverter {

    @Autowired
    private ModifierConverter modifierConverter;
    @Autowired
    private AnnotationsConverter annotationsConverter;
    @Autowired
    private FieldConverter fieldConverter;
    @Autowired
    private MethodConverter methodConverter;
    @Autowired
    private TypeConverter typeConverter;

    public CompilationUnit classToCompilationUnit(WCLClass model) {

        CompilationUnit compilationUnit = compilationUnitForPackage(model.getPackageName(), model.getImports());
        ClassOrInterfaceDeclaration clazz = createClassDeclaration(model, compilationUnit);

        createGenericTypes(model, clazz);
        createSuperClass(model, clazz);
        createImplementingInterfaces(model, clazz);
        createAnnotations(model, clazz);
        createMethods(model, clazz);

        return compilationUnit;
    }

    private void createMethods(WCLClass model, ClassOrInterfaceDeclaration clazz) {
        for (WCLMethod method : model.getMethods()) {
            ASTHelper.addMember(clazz, methodConverter.createMethodDeclaration(method));
        }
    }

    private void createAnnotations(WCLClass model, ClassOrInterfaceDeclaration clazz) {
        clazz.setAnnotations(annotationsConverter.createAnnotationsExpr(model.getAnnotations()));
        for (WCLField field : model.getFields()) {
            ASTHelper.addMember(clazz, fieldConverter.createFieldDeclaration(field));
        }
    }

    private void createImplementingInterfaces(WCLClass model, ClassOrInterfaceDeclaration clazz) {
        List<ClassOrInterfaceType> interfaces = new ArrayList<ClassOrInterfaceType>();
        for (WCLInterfaceType interfaceType : model.getInterfaces()) {
            interfaces.add(new ClassOrInterfaceType(interfaceType.getName()));
        }
        clazz.setImplements(interfaces.size() > 0 ? interfaces : null);
    }

    private void createSuperClass(WCLClass model, ClassOrInterfaceDeclaration clazz) {
        if (model.getSuperClazz() != null) {
            List<ClassOrInterfaceType> extendsList = new ArrayList<ClassOrInterfaceType>();
            ClassOrInterfaceType superClassType = new ClassOrInterfaceType(model.getSuperClazz().getName());

            List<Type> types = new ArrayList<Type>();
            for (String genericType : model.getSuperClazz().getGenericTypes()) {
                types.add(ASTHelper.createReferenceType(genericType, 0));
            }
            superClassType.setTypeArgs(types);

            extendsList.add(superClassType);
            clazz.setExtends(extendsList);
        }
    }

    private void createGenericTypes(WCLClass model, ClassOrInterfaceDeclaration clazz) {

        if (model.getGenericTypes().isEmpty()) {
            clazz.setTypeParameters(null);
            return;
        }

        List<TypeParameter> typeParameters = new ArrayList<TypeParameter>();
        for (WCLGenericType genericType : model.getGenericTypes()) {
            typeParameters.add(typeConverter.createTypeParameter(genericType));
        }
        clazz.setTypeParameters(typeParameters);
    }

    private ClassOrInterfaceDeclaration createClassDeclaration(WCLClass model, CompilationUnit compilationUnit) {
        ClassOrInterfaceDeclaration type = new ClassOrInterfaceDeclaration(ModifierSet.PUBLIC, false, model.getName());
        ASTHelper.addTypeDeclaration(compilationUnit, type);
        return type;
    }

    private CompilationUnit compilationUnitForPackage(String packageName, List<WCLImport> imports) {
        CompilationUnit compilationUnit = new CompilationUnit();
        compilationUnit.setPackage(new PackageDeclaration(ASTHelper.createNameExpr(packageName)));

        List<ImportDeclaration> importDeclarations = new ArrayList<ImportDeclaration>();
        for (WCLImport imprt : imports) {
            importDeclarations.add(new ImportDeclaration(
                    new NameExpr(imprt.getName()), imprt.isStaticable(), imprt.isAsterix()));

        }
        compilationUnit.setImports(importDeclarations);


        return compilationUnit;
    }

}
